package com.xtaller.lazy.config.ex;

import com.alibaba.fastjson.JSONObject;
import com.xtaller.lazy.anync.ExTask;
import com.xtaller.lazy.lib.R;
import com.xtaller.lazy.lib.convert.J;
import com.xtaller.lazy.lib.convert.P;
import com.xtaller.lazy.model.system.ExLog;
import lombok.var;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.io.BufferedReader;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @version V1.0
 * @author: Taller
 * @date: 2019-04-21
 * @Description: 控制器统一try catch 异常处理
 */
@ControllerAdvice
@ResponseBody
public class ControllerExceptionHandler {
    @Autowired private ExTask exTask;

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity handleMissingServletRequestParameterException(MissingServletRequestParameterException e, HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.BAD_REQUEST,"缺少必要的请求参数");
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity handleHttpMessageNotReadableException(
            HttpMessageNotReadableException e,
            HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.BAD_REQUEST,"参数解析失败");
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.BAD_REQUEST,"参数验证失败");
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public ResponseEntity handleBindException(BindException e, HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.BAD_REQUEST,"参数绑定失败");
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity handleServiceException(ConstraintViolationException e, HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.BAD_REQUEST,"约束验证失败");
    }

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ValidationException.class)
    public ResponseEntity handleValidationException(ValidationException e, HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.BAD_REQUEST,"参数验证失败");
    }

    /**
     * 405 - Method Not Allowed
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e,
                                                                       HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.METHOD_NOT_ALLOWED,String.format("该API不支持[%s]请求方法",e.getMethod()));
    }

    /**
     * 415 - Unsupported Media Type
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ResponseEntity handleHttpMediaTypeNotSupportedException(Exception e, HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.UNSUPPORTED_MEDIA_TYPE,"不支持当前请求数据类型");
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ServiceException.class)
    public ResponseEntity handleServiceException(ServiceException e, HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.INTERNAL_SERVER_ERROR,"业务服务异常");
    }

    /**
     * 500 - Internal Server Error
     */
//    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//    @ExceptionHandler(Exception.class)
//    public ResponseEntity handleException(Exception e, HttpServletRequest request) {
//        log(request, e);
//        return R.error(HttpStatus.INTERNAL_SERVER_ERROR,"服务器异常");
//    }

    /**
     * 操作数据库出现异常:名称重复，外键关联
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(DataIntegrityViolationException.class)
    public ResponseEntity handleException(DataIntegrityViolationException e,
                                          HttpServletRequest request) {
        log(request, e);
        return R.error(HttpStatus.BAD_REQUEST,"操作数据库出现异常,数据回滚初始状态.");
    }

    private void log(HttpServletRequest request,Exception e){
        var method = request.getMethod();
        var path = request.getServletPath();
        var params = request.getParameterMap();
        var items = params.keySet();
        var token = request.getHeader("token");
        BufferedReader br = null;
        try {
            br = request.getReader();
        }catch (Exception ex){

        }
        var paramList = J.list();
        items.stream().forEach(key -> {
            var datas = params.get(key);
            var p = J.create(key, datas);
            paramList.add(p);
        });
        var exLog = new ExLog();
        exLog.setToken(token);
        exLog.setMethod(method);
        exLog.setParams(paramList.toString());
        exLog.setUrl(path);
        exLog.setBody(charReader(br));
        var exItem = e.toString().split(":");
        exLog.setEx(exItem.length > 0 ? exItem[0] : e.toString());
        exLog.setMessage(e.getMessage() == null ? e.toString() : e.getMessage());
        exLog.setIp(getIpAddr(request));
        exTask.createEx(exLog);
    }

    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if(ip.equals("127.0.0.1")){
                //根据网卡取本机配置的IP
                InetAddress inet=null;
                try {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                ip= inet.getHostAddress();
            }
        }
        // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if(ip != null && ip.length() > 15){
            if(ip.indexOf(",")>0){
                ip = ip.substring(0,ip.indexOf(","));
            }
        }
        return ip;
    }
    private String charReader(BufferedReader br) {

        String str, wholeStr = "";
        try {
            while ((str = br.readLine()) != null) {
                wholeStr += str;
            }
            return wholeStr;
        }catch (Exception ex){
            return "";
        }
    }
}
